/*****************************
* randomtestadventurer.c
* Author: Matthew Tanner
* CS362 - Assignment #4
*
* Random test of zzzadventurercardplay function in dominion.c
*
* Randomizes hand, deck, and discard count.
* Fills hand, deck, and discard piles with random piles.
* Calls oracle checkAdventurer to test output.
*
* Output is checked as follows:
* 	- number of total cards in deck and discard piles reduced by 0, 1, or 2 
		depending on how many total treasure cards in these piles
	- number of cards in hand increased by 0, 1, or 2 depending on how many
		treasure cards in deck/discard piles
	- number of treasure cards increased/decreased in piles as described above.
*
* The following bugs have been found on not adressed yet:
* 	- Sizes of > 475 for deck, discard or hand counts cause seg fault
* 	- 0 treasure cards in deck, and discard piles not handled in zzzadventurer
		function. Causes trashing of top two cards in hand.
*
* Input values have been adjusted for now to avoid these two scenarios
* 
******************************/

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"
#include "tests.h"

#define NUM_TYPE_CARDS 13  // 3 treasure + 10 adventurer cards

// set this to 1 to get summary of each iteration to stdout
#define NOISY 0

// set his to get contents of piles for each iteration
#define SHOW_PILES 0

// used to set psuedo random number generator sequence of numbers
#define SEED 100

/*****************************
* Creates random inputs. Parameter numIterations determines how many 
* iterations will be made.
******************************/
void randomtestadventurer(int numIterations){

	//set minimum req'd variables and initialize game.

	struct gameState g;
	int players = 2;
	int k[10] = {adventurer, council_room, sea_hag, gardens, mine, remodel, smithy, village, baron, great_hall};
	int res, p, n, i, c;

	if((res = initializeGame(players, k, SEED, &g)) == -1){
	printf("Error initializing game\n");
	exit(-1);
	}

	SelectStream(2);
	PutSeed(SEED);

	//repeat this <numIterations> times
	for(n = 0; n < numIterations; n++){

		g.coins = 0;
		/***************************************
		* randomly set handcount, deckcount, discardcount from 0 to max values
		* NOTE** program crashes if more than 475 cards provided
		*****************************************/
	    p = floor(Random() * 2)+1;
	    g.whoseTurn = p;
	    
	    // Set number of cards in piles. (475 is reduced max piles size to
	    // avoid seg fault)
	    g.deckCount[p] = floor(Random() * 475); 
	    g.discardCount[p] = floor(Random() * 475); 
	    g.handCount[p] = floor(Random() * 475);	    

		// fill deck with random cards (ignores supply count)
		 for(i = 0; i < g.deckCount[p]; i++){
		 	c = floor(Random() * NUM_TYPE_CARDS);
		 	g.deck[p][i] = selectCard(c, k, &g);
			//printf("%d/%d ", g.deck[p][i], n);
		}

		/// fill discard with random cards (ignores supply count)
		for(i = 0; i < g.discardCount[p]; i++){
		  	c = floor(Random() * NUM_TYPE_CARDS);
			g.discard[p][i] = selectCard(c, k, &g);
			// printf("%d/%d ", g.discard[p][i], n);
		}	

		// fill hand with random cards (ignores supply count)
		for(i = 0; i < g.handCount[p]; i++){
		 	c = floor(Random() * NUM_TYPE_CARDS);
			g.hand[p][i] = selectCard(c, k, &g);
			//printf("%d/%d ", g.hand[p][i], n);
		}	

		//avoid case of 0 treasure cards in deck and discard (known bug)
		if((numTreasureCards(g.deck[p], g.deckCount[p]) == 0) && 
			(numTreasureCards(g.discard[p], g.discardCount[p]) == 0)){
			if(g.deckCount[p] == 0){
				g.deckCount[p] = 1;

			}
			g.deck[p][0] = copper;
		}

	    #if NOISY
		    printf("Iteration#:%d\nPRE... player:%d deck:%d discard:%d hand:%d\n", 
		    	n+1, p, g.deckCount[p], g.discardCount[p], g.handCount[p]);
	    #endif

	   	// If set, outputs contents of each pile before function call
	    #if SHOW_PILES
		    printf("Pre Hand: ");
		    for(int i=0; i < g.handCount[p]; i++){
		      printf("%d, ", g.hand[p][i]);
		    }
		    printf("\n");

		    printf("Pre Deck: ");
		    for(int i=0; i < g.deckCount[p]; i++){
		      printf("%d, ", g.deck[p][i]);
		    }
		    printf("\n");

		      printf("Pre Discard: ");
		    for(int i=0; i < g.discardCount[p]; i++){
		      printf("%d, ", g.discard[p][i]);
		    }
		    printf("\n");
		#endif

	    // call oracle
		checkAdventurer(p, &g);
	}
}

/**************************
* Oracle function for random tests of adventurer card function
***************************/
void checkAdventurer(int p, struct gameState *post){

	struct gameState pre;
	int r, prePilesTreasure, postPilesTreasure, preHandTreasure,
		postHandTreasure, prePilesCount, postPilesCount, diff;

	// make copy of gamestate
	memcpy(&pre, post,sizeof(struct gameState));

	// call function (cardeffect calls all card functions)
	r = cardEffect(adventurer, 0, p, 0, post, 0, 0);

	// get number of treasure cards in piles (deck and discard) and hand
	prePilesTreasure = (numTreasureCards(pre.deck[p], pre.deckCount[p])) + 
		(numTreasureCards(pre.discard[p], pre.discardCount[p]));
	postPilesTreasure = (numTreasureCards(post->deck[p], post->deckCount[p])) + 
		(numTreasureCards(post->discard[p], post->discardCount[p]));
	preHandTreasure = numTreasureCards(pre.hand[p], pre.handCount[p]);
	postHandTreasure = numTreasureCards(post->hand[p], post->handCount[p]);

	// if set, outputs summary of changes in treasure cards and pile counts
	#if NOISY
		printf("pre piles count:%d post:%d\n", pre.deckCount[p] + pre.discardCount[p],
			post->deckCount[p] + post->discardCount[p]);
		printf("pre treasure count in piles:%d ", prePilesTreasure);
		printf("post:%d\n", postPilesTreasure);
		printf("pre hand count:%d ", pre.handCount[p]);
		printf("post hand count:%d\n", post->handCount[p]);
		printf("pre treasure count in hand:%d ", preHandTreasure);
		printf("post:%d\n", postHandTreasure);
	    printf("POST...player:%d deck:%d discard:%d hand:%d\n\n", 
			p, post->deckCount[p], post->discardCount[p], post->handCount[p]);
	#endif

    // Assert total handcount increased correctly
	if(prePilesTreasure >= 2){ 
		diff = 2;
		assert((post->handCount[p] - pre.handCount[p]) == diff);
	}
	else { // 1 or 0 treasure cards in piles, so 1 or 0 added to hand
	 	diff = prePilesTreasure;
	 	assert(post->handCount[p] == (pre.handCount[p] + diff));
	}

	// Assert total discard and deck pile counts decreased correctly
	if(prePilesTreasure >= 2){ 
		diff = 2;
		prePilesCount = pre.deckCount[p] + pre.discardCount[p];
		postPilesCount = post->deckCount[p] + post->discardCount[p];
		assert(prePilesCount - postPilesCount == diff);
	}
	else { // 1 or 0 treasure cards in piles, so 1 or 0 less in piles
	 	diff = prePilesTreasure;
	 	assert((pre.deckCount[p] + pre.discardCount[p]) - 
	 		(post->deckCount[p] + post->discardCount[p]) == diff);
	}	

	// Assert total number of treasure cards in hand increased correctly
	if(prePilesTreasure >= 2){ 
		diff = 2;
		assert(postHandTreasure - preHandTreasure == diff);

	}else { // 1 or 0 treasure cards in piles, so 1 or 0 more in hand
	 	diff = prePilesTreasure;
	 	assert(postHandTreasure - preHandTreasure == diff);
	}

	// Assert total number of treasure cards in discard/deck piles decreased correctly
	if(prePilesTreasure >= 2){ 
		diff = 2;
		assert(prePilesTreasure - postPilesTreasure == diff);

	}else { // 1 or 0 treasure cards in piles, so 1 or 0 less in piles
	 	diff = prePilesTreasure;
	 	assert((prePilesTreasure - postPilesTreasure) == diff);
	}	

	assert(r == 0);

}


// returns card based on integer input (0-9 adventurer cards, 10-12 treasure cards)
// used to fill piles
enum CARD selectCard(int n, int k[], struct gameState* g){

	if(n < 10){
		return k[n];
	}
	else{
		switch(n){
			case 10:
			g->coins += 1;
			return copper;
			case 11:
			g->coins += 2;
			return silver;
			case 12:
			g->coins += 3;
			return gold;
			default:
			return -1;
		}
	}
}

// Returns number of treasure cards in given pile.
int numTreasureCards(int* pile, int count){
	int i, treasurecards;
	treasurecards = 0;
	// count treasure cards in pile
	for(i = 0; i < count ;i++){
		if(pile[i] == copper || pile[i] == silver 
			|| pile[i] == gold){
			treasurecards++;
		}
	}

	return treasurecards;
}
